/*
 *  Netzpult - Your desk on the web
 *  Copyright (C) 2011  Ulrich Hilger, http://uhilger.de
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see http://www.gnu.org/licenses/
 */

package de.uhilger.netzpult.client;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;

/**
 * Class HttpRequest makes it more convenient to form and send a HTTP request
 * using Google Web Toolkit (GWT).
 * 
 * @author Copyright (c) Ulrich Hilger, http://uhilger.de
 * @author Published under the terms and conditions of
 * the <a href="http://www.gnu.org/licenses/" target="_blank">GNU General Public License</a>
 */
public class HttpRequest {

  /** Request type constant */
  public static final int REQUEST_TYPE_GET = 0;
  /** Request type constant */
  public static final int REQUEST_TYPE_POST = 1;

  private static final String QUERY_INDICATOR = "?";
  private static final String PARAM_CONCATENATOR = "&";
  private static final String VALUE_INDICATOR = "=";
  
  private List<Parameter> params;
  private String urlStr;
  private int type = REQUEST_TYPE_GET;
  private List<Parameter> headers;
  private RequestCallback callback;

  /**
   * Create a new object of class HttpRequest
   */
  public HttpRequest() {
    super();
    params = new ArrayList<Parameter>();
    headers = new ArrayList<Parameter>();
  }

  /**
   * Create a new object of class HttpRequest
   * @param urlStr  the url string to send the request to
   * @param callback   the object that will handle a reply to this request
   */
  public HttpRequest(String urlStr, RequestCallback callback) {
    this();
    setUrlStr(urlStr);
    setCallback(callback);
  }

  /**
   * Send this HttpRequest
   * @throws RequestException
   */
  public void send() throws RequestException {
    RequestBuilder builder = null;
    //Window.alert("HttpRequest.send " + getRequestString());
    builder = new RequestBuilder(getRequestMethod(), getRequestString());
    Iterator<Parameter> i = headers.iterator();
    while(i.hasNext()) {
        Parameter p = i.next();
        builder.setHeader(p.getKey(), p.getValue());
    }
    if(type == REQUEST_TYPE_GET) {
      builder.sendRequest(null, getCallback());
    } else {
      builder.sendRequest(getQuery(), getCallback());
    }
  }

  /* ---------- setters ----------------- */

  /**
   * Add a parameter to this request
   * @param key  the parameter key
   * @param value  the parameter value
   */
  public void addParameter(String key, String value) {
    //Window.alert("HttpRequest.addParameter key=" + key + ", value=" + value);
    params.add(new Parameter(key, value));
  }

  /**
   * The the type of this request
   * @param type  the type to set this request to, one of REQUEST_TYPE_GET and REQUEST_TYPE_POST
   */
  public void setType(int type) {
    this.type = type;
  }

  /**
   * Set the URL string that request should be sent to
   * @param urlStr  the URL to send this request to
   */
  public void setUrlStr(String urlStr) {
    this.urlStr = urlStr;
  }

  /**
   * Set the object that will handle a reply to this request
   * @param callback  the object that handles a reply to this request
   */
  public void setCallback(RequestCallback callback) {
    this.callback = callback;
  }

  /**
   * Add a header to this request
   * @param key  the header key
   * @param value  the header value
   */
  public void addHeader(String key, String value) {
    headers.add(new Parameter(key, value));
  }

  /* ---------- getters ------------------ */

  /**
   * Get the type of this request
   * @return  the request type,  one of REQUEST_TYPE_GET and REQUEST_TYPE_POST
   */
  public int getType() {
    return type;
  }

  /**
   * Get the object that handles a reply from this request
   * @return  the object that handles a reply
   */
  public RequestCallback getCallback() {
    return callback;
  }

  /**
   * Get the value of the parameter with a given key
   * @param key  the key to get the value for
   * @return  the value of the parameter with the given key
   */
  public String getParameterValue(String key) {
    return getListValue(params, key);
  }

  /**
   * Get the parameter with a given key
   * @param key  the key to get the parameter for
   * @return  the parameter with the given key
   */
  public Parameter getParameter(String key) {
    return getListParameter(params, key);
  }

  /**
   * Get the URL this request is to be sent to
   * @return  the URL this request is to be sent to
   */
  public String getUrlStr() {
    return urlStr;
  }

  /**
   * Get the header with a given key
   * @param key the key of the header to get
   * @return the header as a Parameter making up the header as a key/value pair
   */
  public Parameter getHeader(String key) {
    return getListParameter(headers, key);
  }

  /**
   * Get the value of the header with a given key
   * @param key  the key to get the header value for
   * @return  the header value with the given key
   */
  public String getHeaderValue(String key) {
    return getListValue(headers, key);
  }

  /* ---------------- where the music plays ------------- */

  /**
   * Get the parameter with a given key out of a given list
   * @param list  the list to get the parameter from, one of params and headers
   * @param key  the key to get the parameter for
   * @return  the parameter
   */
  private Parameter getListParameter(List<Parameter> list, String key) {
    Parameter p = null;
    int index = list.indexOf(new Parameter(key,""));
    if(index > -1) {
        p = list.get(index);
    }
    return p;
  }

  /**
   * Get the parameter value with a given key out of a given list
   * @param list  the list to get the parameter value from, one of params and headers
   * @param key  the key to get the parameter for
   * @return  the parameter value
   */
  public String getListValue(List<Parameter> list, String key) {
    String value = null;
    int index = list.indexOf(new Parameter(key,""));
    if(index > -1) {
        Parameter p = list.get(index);
        value = p.getValue();
    }
    return value;
  }

  /**
   * Get the request method for this request
   * @return  the request method, one of RequestBuilder.GET and RequestBuilder.POST
   */
  private RequestBuilder.Method getRequestMethod() {
    RequestBuilder.Method method = null;
    switch(type) {
      case REQUEST_TYPE_GET:
        method = RequestBuilder.GET;
        break;
      case REQUEST_TYPE_POST:
        method = RequestBuilder.POST;
        addHeader("Content-Type", "application/x-www-form-urlencoded");
        break;
      default:
        method = RequestBuilder.GET;
        break;
    }
    return method;
  }

  /**
   * Build the URL for this <code>HttpRequest</code>,
   * in case of HTTP GET this will include the query part
   * @return  the request URL
   */
  private String getRequestString() {
    StringBuffer req = new StringBuffer(getUrlStr());
    if(type == REQUEST_TYPE_GET) {
      String query = getQuery();
      if(query != null && query.length() > 0) {
        req.append(QUERY_INDICATOR);
        req.append(query);
      }
    }
    return req.toString();
  }

  /**
   * Build the request query in the form
   * <code>param1Key=param1Value&param2Key=param2Value[..]</code>
   * @return the request query
   */
  private String getQuery() {
    return concatenate(params);
  }

  /**
   * Concatenate the parameters of the given list
   * @param list  the list to concatenate paramteres from
   * @return  the concatenated parameters in the form 'key=value&key=value..&key=value'
   */
  private String concatenate(List<Parameter> list) {
    StringBuffer buf = new StringBuffer();
    Iterator<Parameter> i = list.iterator();
    while (i.hasNext()) {
        if (buf.length() > 0) {
          buf.append(PARAM_CONCATENATOR);
        }
        Parameter p = i.next();
        buf.append(p.getKey());
        buf.append(VALUE_INDICATOR);
        buf.append(p.getValue());
    }
    return buf.toString();
  }
  
}
